package com.ly.heart.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ly.common.PageParam;
import com.ly.common.core.domain.AjaxResult;
import com.ly.common.core.domain.entity.SysDept;
import com.ly.common.core.domain.entity.SysUser;
import com.ly.common.exception.CustomException;
import com.ly.common.utils.DateUtils;
import com.ly.common.utils.SecurityUtils;
import com.ly.common.utils.StringUtils;
import com.ly.common.utils.erp.CommonUtil;
import com.ly.common.utils.erp.ConstUtil;
import com.ly.common.utils.erp.KingConstUtil;
import com.ly.common.utils.erp.SnowflakeIdWorker;
import com.ly.heart.domain.*;
import com.ly.heart.domain.dto.KingFinanceReceiptRefundDto;
import com.ly.heart.domain.dto.StartProcessDto;
import com.ly.common.core.domain.ActCurrentTaskVo;
import com.ly.heart.domain.vo.KingFinanceReceiptRefundVo;
import com.ly.heart.mapper.KingFinanceReceiptRefundMapper;
import com.ly.heart.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ly.heart.service.king.ReceiptRefundPushService;
import com.ly.heart.util.ChineseToPinyin;
import com.ly.system.service.ISysDeptService;
import com.ly.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>
 * 收款退款单 服务实现类
 * </p>
 * @author WuZH
 * @since 2024-08-04
 */
@Service
@Slf4j
public class KingFinanceReceiptRefundServiceImpl extends ServiceImpl<KingFinanceReceiptRefundMapper, KingFinanceReceiptRefundEntity> implements IKingFinanceReceiptRefundService {

    @Resource
    private KingFinanceReceiptRefundMapper kingFinanceReceiptRefundMapper;
    @Resource
    private IKingFinanceReceiptDetailService iKingFinanceReceiptDetailService;
    @Resource
    private IKingFinanceReceiptRefundDetailService iKingFinanceReceiptRefundDetailService;
    @Resource
    private IActModelService iActModelService;
    @Resource
    private ICustomerService iCustomerService;
    @Resource
    private ISupplierService iSupplierService;
    @Resource
    private ISysDeptService iSysDeptService;
    @Resource
    private ISysUserService iSysUserService;
    @Resource
    private IKingBaseTransactionsCompanyService iKingBaseTransactionsCompanyService;
    @Resource
    private IKingBaseBankService iKingBaseBankService;
    @Resource
    private IActTaskCommentService iActTaskCommentService;
    @Resource
    private ISaleOrderService iSaleOrderService;
    @Resource
    private ReceiptRefundPushService receiptRefundPushService;


    @Override
    public AjaxResult queryList (KingFinanceReceiptRefundEntity kingFinanceReceiptRefundEntity) {
        kingFinanceReceiptRefundEntity.setIsUsed ( ConstUtil.IS_USED_YES );
        kingFinanceReceiptRefundEntity.setPage ( ( kingFinanceReceiptRefundEntity.getPage () - 1 ) * kingFinanceReceiptRefundEntity.getLimit () );
        List<KingFinanceReceiptRefundEntity> kingFinanceReceiptRefundEntities = kingFinanceReceiptRefundMapper.queryList ( kingFinanceReceiptRefundEntity );
        kingFinanceReceiptRefundEntities.forEach ( refundEntity -> {
            String transactionCompanyName = getCompanyName ( refundEntity.getTransactionCompanyType (), refundEntity.getTransactionCompanyId (), 1 );
            refundEntity.setTransactionCompanyName ( transactionCompanyName );
            String receiptCompanyName = getCompanyName ( refundEntity.getReceiptDeptType (), refundEntity.getReceiptDeptId (), 1 );
            refundEntity.setReceiptDeptName ( receiptCompanyName );
            //处理最后一个审批人
            ActTaskCommentEntity actTaskCommentEntity = iActTaskCommentService.queryLastComment ( refundEntity.getInstanceId () );
            if ( actTaskCommentEntity != null ) {
                refundEntity.setApprovedUser ( actTaskCommentEntity.getApprovedUser () );
                refundEntity.setApprovedRemark ( actTaskCommentEntity.getRemark () );
            }
            //任务信息
            ActCurrentTaskVo currentTaskVo = iActModelService.getCurrentTaskByInstanceId ( refundEntity.getInstanceId () );
            Optional.ofNullable ( currentTaskVo ).ifPresent ( currentTask -> {
                refundEntity.setCurrentTask ( currentTask );
            } );
        } );
        Integer count = kingFinanceReceiptRefundMapper.queryCount ( kingFinanceReceiptRefundEntity );
        PageParam pageParam = new PageParam ();
        pageParam.setCurrent ( kingFinanceReceiptRefundEntity.getPage () ).setSize ( kingFinanceReceiptRefundEntities.size () ).setRecords ( kingFinanceReceiptRefundEntities ).setTotal ( count );
        return AjaxResult.success ( pageParam );
    }

    @Override
    @Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AjaxResult updateSingle (KingFinanceReceiptRefundDto kingFinanceReceiptRefundDto) {
        KingFinanceReceiptRefundEntity receiptRefundEntity = kingFinanceReceiptRefundDto.getKingFinanceReceiptRefundEntity ();
        receiptRefundEntity.setUpdateDate ( DateUtils.getNowDate () );
        receiptRefundEntity.setUpdateUserId ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
        boolean b = this.updateById ( receiptRefundEntity );
        if ( ! b ) {
            throw new CustomException ( "修改收款退款单基本信息失败！" );
        }
        if ( receiptRefundEntity.getSourceDocType () == KingConstUtil.RECEIPT_REFUND_TYPE_PAY ) {
            checkReceiptToRefund ( kingFinanceReceiptRefundDto.getKingFinanceReceiptRefundDetailEntities () );
        }
        updateReceiptDetail ( kingFinanceReceiptRefundDto.getKingFinanceReceiptRefundDetailEntities (), receiptRefundEntity.getId () );
        return AjaxResult.success ();
    }

    /**
     * 查询详情，推送金蝶
     * @param id
     * @return
     */
    @Override
    public KingFinanceReceiptRefundEntity querySingleById (String id) {
        KingFinanceReceiptRefundEntity receiptRefundEntity = kingFinanceReceiptRefundMapper.querySingle ( id );
        //处理收款单位和往来单位编码
        String receiptCode = getCompanyName ( receiptRefundEntity.getReceiptDeptType (), receiptRefundEntity.getReceiptDeptId (), 2 );
        receiptRefundEntity.setReceiptDeptCode ( receiptCode );
        String transactionCompanyId = getCompanyName ( receiptRefundEntity.getTransactionCompanyType (), receiptRefundEntity.getTransactionCompanyId (), 2 );
        receiptRefundEntity.setTransactionCompanyCode ( transactionCompanyId );
        return receiptRefundEntity;
    }

    /**
     * 收款退款单审核后业务处理
     * 1、处理销售订单的退款金额
     * 2、更新状态
     * 3、推送金蝶
     * @param id
     * @return
     */
    @Override
    public AjaxResult approve (String id) {
        List<KingFinanceReceiptRefundDetailEntity> list = iKingFinanceReceiptRefundDetailService.list ( new LambdaQueryWrapper<KingFinanceReceiptRefundDetailEntity> ().
                eq ( KingFinanceReceiptRefundDetailEntity :: getReceiptRefundId, id ) );
        list.forEach ( detail -> {
            if ( StringUtils.isNotBlank ( detail.getSaleId () ) ) {
                SaleOrderEntity saleOrderEntity = iSaleOrderService.getById ( detail.getSaleId () );
                String totalRefundAmount = saleOrderEntity.getTotalRefundAmount (); // 累计退款金额
                String actualRefundMoney = detail.getActualRefundMoney (); //本次实际退款金额
                boolean update = iSaleOrderService.update ( new LambdaUpdateWrapper<SaleOrderEntity> ().
                        set ( SaleOrderEntity :: getTotalRefundAmount, CommonUtil.sumStr ( totalRefundAmount, actualRefundMoney, 2 ) ).
                        eq ( SaleOrderEntity :: getId, detail.getSaleId () ) );
                if ( ! update ) {
                    throw new CustomException ( "修改销售订单退款金额失败！" );
                }
                log.info ( "收款退款单更新销售订单退款金额成功。" );
            }
        } );
        boolean update = this.update ( new LambdaUpdateWrapper<KingFinanceReceiptRefundEntity> ().
                set ( KingFinanceReceiptRefundEntity :: getState, ConstUtil.STATE_FINISH ).
                eq ( KingFinanceReceiptRefundEntity :: getId, id ) );
        if ( ! update ) {
            throw new CustomException ( "修改收款退款单审核状态失败！" );
        }
        log.info ( "收款退款单修改审批状态成功。" );
        //推送金蝶
        receiptRefundPushService.push ( id );
        return AjaxResult.success ( "收款退款单审核完成并成功推送金蝶" );
    }

    @Override
    @Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AjaxResult draft (KingFinanceReceiptRefundDto kingFinanceReceiptRefundDto) {
        KingFinanceReceiptRefundEntity kingFinanceReceiptRefundEntity = kingFinanceReceiptRefundDto.getKingFinanceReceiptRefundEntity ();
        kingFinanceReceiptRefundEntity.setId ( SnowflakeIdWorker.getInstance ().nextStringId () );
        kingFinanceReceiptRefundEntity.setCreateDate ( DateUtils.getNowDate () );
        kingFinanceReceiptRefundEntity.setCreateUserId ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
        int count = this.count ();
        String py = ChineseToPinyin.getFirstLetter ( "收款退款单" );
        String format = String.format ( "%08d", count );
        kingFinanceReceiptRefundEntity.setDocNo ( py.concat ( format ) );
        if ( kingFinanceReceiptRefundEntity.getSourceDocType () == KingConstUtil.RECEIPT_REFUND_TYPE_PAY ) {
            checkReceiptToRefund ( kingFinanceReceiptRefundDto.getKingFinanceReceiptRefundDetailEntities () );
        }
        updateReceiptDetail ( kingFinanceReceiptRefundDto.getKingFinanceReceiptRefundDetailEntities (), kingFinanceReceiptRefundEntity.getId () );
        kingFinanceReceiptRefundEntity.setState ( ConstUtil.STATE_IN_DRAFT );
        boolean save = this.save ( kingFinanceReceiptRefundEntity );
        if ( ! save ) {
            throw new CustomException ( "暂存收款退款单失败！" );
        }
        return AjaxResult.success ( "暂存收款退款单成功", kingFinanceReceiptRefundEntity.getId () );
    }

    @Override
    @Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AjaxResult submit (KingFinanceReceiptRefundEntity kingFinanceReceiptRefundEntity) {
        if ( StringUtils.isBlank ( kingFinanceReceiptRefundEntity.getId () ) ) {
            return AjaxResult.error ( "请先暂存该单据！" );
        }
        //处理审批流
        StartProcessDto startDto = new StartProcessDto ().
                setBusinessId ( kingFinanceReceiptRefundEntity.getId () ).
                setBusinessType ( kingFinanceReceiptRefundEntity.getBusinessType () ).setModelId ( kingFinanceReceiptRefundEntity.getModelId () ).
                setBusinessName ( "收款退款单".concat ( kingFinanceReceiptRefundEntity.getId () ) );
        String instanceId = iActModelService.startProcess ( startDto );
        boolean update = this.update ( new LambdaUpdateWrapper<KingFinanceReceiptRefundEntity> ().
                set ( KingFinanceReceiptRefundEntity :: getInstanceId, instanceId ).
                set ( KingFinanceReceiptRefundEntity :: getState, ConstUtil.STATE_IN_PROCESS ).
                eq ( KingFinanceReceiptRefundEntity :: getId, kingFinanceReceiptRefundEntity.getId () ) );
        if ( ! update ) {
            throw new CustomException ( "提交收款退款单失败！" );
        }
        return AjaxResult.success ();
    }

    @Override
    public KingFinanceReceiptRefundVo querySingle (String id) {
        KingFinanceReceiptRefundVo vo = new KingFinanceReceiptRefundVo ();
        KingFinanceReceiptRefundEntity receiptRefundEntity = kingFinanceReceiptRefundMapper.querySingle ( id );
        String transactionCompanyName = getCompanyName ( receiptRefundEntity.getTransactionCompanyType (), receiptRefundEntity.getTransactionCompanyId (), 1 );
        receiptRefundEntity.setTransactionCompanyName ( transactionCompanyName );
        String receiptCompanyName = getCompanyName ( receiptRefundEntity.getReceiptDeptType (), receiptRefundEntity.getReceiptDeptId (), 1 );
        receiptRefundEntity.setReceiptDeptName ( receiptCompanyName );
        KingFinanceReceiptRefundDetailEntity receiptRefundDetailEntity = new KingFinanceReceiptRefundDetailEntity ();
        receiptRefundDetailEntity.setReceiptRefundId ( id );
        List<KingFinanceReceiptRefundDetailEntity> list = iKingFinanceReceiptRefundDetailService.selectReceiptRefundList ( receiptRefundDetailEntity );
        vo.setKingFinanceReceiptRefundEntity ( receiptRefundEntity ).setKingFinanceReceiptRefundDetailEntities ( list );
        return vo;
    }

    /**
     * 新增付款退款单时校验规则
     * 退款单可退全部，可退其中一条明细，检查明细
     * 同一个付款单位（退款时供应商就是付款单位），实退金额不能大于该付款单未核销部分金额
     * @param kingFinanceReceiptRefundDetailEntities
     */
    private void checkReceiptToRefund (List<KingFinanceReceiptRefundDetailEntity> kingFinanceReceiptRefundDetailEntities) {
        if ( ! CollectionUtils.isEmpty ( kingFinanceReceiptRefundDetailEntities ) ) {
            kingFinanceReceiptRefundDetailEntities = kingFinanceReceiptRefundDetailEntities.stream ().filter ( detail -> StringUtils.isNotBlank ( detail.getSourceDetailId () ) ).collect ( Collectors.toList () );
            //源付款单明细行实退金额总和
            double sourceActualMoney = 0;
            for ( int i = 0; i < kingFinanceReceiptRefundDetailEntities.size (); i++ ) {
                //1.退款单明细行
                KingFinanceReceiptRefundDetailEntity receiptRefundDetailEntity = kingFinanceReceiptRefundDetailEntities.get ( i );
                String actualMoney = receiptRefundDetailEntity.getActualRefundMoney (); // 明细行每行实退金额
                //2.源付款单明细
                KingFinanceReceiptDetailEntity receiptDetailEntity = iKingFinanceReceiptDetailService.getById ( receiptRefundDetailEntity.getSourceDetailId () );
                // 所有明细行的实退金额总和
                sourceActualMoney = CommonUtil.sum ( sourceActualMoney, Double.parseDouble ( receiptDetailEntity.getActualMoney () ) );
                if ( Double.parseDouble ( actualMoney ) > ( Double.parseDouble ( receiptDetailEntity.getActualMoney () ) ) ) {
                    throw new CustomException ( "第" + receiptRefundDetailEntity.getLineNumber () + "行实退金额超限！" );
                }
            }
            //退款单明细行实退金额总和
            double sum = kingFinanceReceiptRefundDetailEntities.stream ().mapToDouble ( amount -> Double.parseDouble ( amount.getActualRefundMoney () ) ).sum ();
            if ( sum > sourceActualMoney ) {
                throw new CustomException ( "实退金额总和不能超过源收款单可退金额！" );
            }
        }
    }

    private void updateReceiptDetail (List<KingFinanceReceiptRefundDetailEntity> kingFinanceReceiptRefundDetailEntities, String receiptRefundId) {
        if ( ! CollectionUtils.isEmpty ( kingFinanceReceiptRefundDetailEntities ) ) {
            iKingFinanceReceiptRefundDetailService.remove ( new LambdaUpdateWrapper<KingFinanceReceiptRefundDetailEntity> ().eq ( KingFinanceReceiptRefundDetailEntity :: getReceiptRefundId, receiptRefundId ) );
            kingFinanceReceiptRefundDetailEntities.forEach ( receiptRefundDetailEntity -> {
                receiptRefundDetailEntity.setId ( SnowflakeIdWorker.getInstance ().nextStringId () );
                receiptRefundDetailEntity.setReceiptRefundId ( receiptRefundId );
                receiptRefundDetailEntity.setCreateDate ( DateUtils.getNowDate () );
                receiptRefundDetailEntity.setCreateUserId ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
                boolean save = iKingFinanceReceiptRefundDetailService.save ( receiptRefundDetailEntity );
                if ( ! save ) {
                    throw new CustomException ( "更新收款退款单明细行失败！" );
                }
            } );
        }
    }

    /**
     * 根据往来单位类型，获取往来单位和付款单位
     * @param companyType：单位类型，根据往来单位或付款单位的类型魔法值
     * @param companyId：单位Id，根据单位类型，选择的具体单位Id
     * @param type：表示要获取名称还是编码；1：名称；2：编码
     * @return
     */
    private String getCompanyName (String companyType, String companyId, Integer type) {
        if ( StringUtils.isBlank ( companyType ) || StringUtils.isBlank ( companyId ) ) {
            return "";
        }
        String transactionCompany = "";
        switch ( companyType ) {
            case "BD_Customer":
                CustomerEntity customerEntity = iCustomerService.getById ( companyId );
                if ( customerEntity != null && type == 1 )
                    transactionCompany = customerEntity.getCustomerName ();
                else if ( customerEntity != null && type == 2 )
                    transactionCompany = customerEntity.getCustomerCode ();
                break;
            case "BD_Supplier":
                SupplierEntity supplierEntity = iSupplierService.getById ( companyId );
                if ( supplierEntity != null && type == 1 )
                    transactionCompany = supplierEntity.getSupplierName ();
                else if ( supplierEntity != null && type == 2 )
                    transactionCompany = supplierEntity.getSupplierCode ();
                break;
            case "BD_Department":
                SysDept sysDept = iSysDeptService.getById ( companyId );
                if ( sysDept != null && type == 1 )
                    transactionCompany = sysDept.getDeptName ();
                else if ( sysDept != null && type == 2 )
                    transactionCompany = sysDept.getDeptCode ();
                break;
            case "BD_Empinfo":
                SysUser sysUser = iSysUserService.getById ( companyId );
                if ( sysUser != null && type == 1 )
                    transactionCompany = sysUser.getUserName ();
                else if ( sysUser != null && type == 2 )
                    transactionCompany = sysUser.getUserCode ();
                break;
            case "FIN_OTHERS":
                KingBaseTransactionsCompanyEntity transactionsCompanyEntity = iKingBaseTransactionsCompanyService.getById ( companyId );
                if ( transactionsCompanyEntity != null && type == 1 )
                    transactionCompany = transactionsCompanyEntity.getTitle ();
                else if ( transactionsCompanyEntity != null && type == 2 )
                    transactionCompany = transactionsCompanyEntity.getCompanyCode ();
                break;
            case "ORG_Organizations":
                SysDept organ = iSysDeptService.getById ( companyId );
                if ( organ != null && type == 1 )
                    transactionCompany = organ.getDeptName ();
                else if ( organ != null && type == 2 )
                    transactionCompany = organ.getDeptCode ();
                break;
            case "BD_BANK":
                KingBaseBankEntity bankEntity = iKingBaseBankService.getById ( companyId );
                if ( bankEntity != null && type == 1 )
                    transactionCompany = bankEntity.getTitle ();
                else if ( bankEntity != null && type == 2 )
                    transactionCompany = bankEntity.getCode ();
                break;
        }
        return transactionCompany;
    }

}
